package cn.cqs.redisitem.utils.redis;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.*;

//@Configuration
//@EnableCaching
//@Slf4j
public class RedisClusterConfig extends CachingConfigurerSupport {
    @Value("${spring.cache.cacheNames}")
    private String cacheDefaultName;
    @Value("${spring.cache.redis.time-to-live}")
    private Duration timeToLive;
    @Resource
   private LettuceConnectionFactory lettuceConnectionFactory;

    /**
     *
     *
     * @return
     */
//  ---------- todo @Bean---------
    public CacheManager cacheManager() {
        RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        //common信息缓存配置
        RedisCacheConfiguration useCacheConfiguration = defaultCacheConfiguration
                // 设置 key为string序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 设置value为json序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        // 设置缓存失效时间，单位是秒
        RedisCacheConfiguration redisCacheConfiguration = useCacheConfiguration.entryTtl(timeToLive);
        Set<String> cacheNames = new HashSet<>();
        List<String> cacheDefaultNames = Arrays.asList(cacheDefaultName.split(","));
        cacheDefaultNames.stream().distinct().forEach(cdn -> {
            redisCacheConfigurationMap.put(cdn, redisCacheConfiguration);
            cacheNames.add(cdn);
        });
        //初始化RedisCacheManager
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(lettuceConnectionFactory).cacheDefaults(defaultCacheConfiguration).initialCacheNames(cacheNames)
                .withInitialCacheConfigurations(redisCacheConfigurationMap).build();
        return redisCacheManager;

    }

    /**
     * 使用Jackson2JsonRedisSerializer
     * LettuceConnectionFactory连接工厂产生模板：序列化redis
     * @param redisConnectionFactory
     * @return
     */
//    @Bean
    public RedisTemplate<String, Object> redisCasheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 任何属性可见
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
//        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        // 在序列化时记录对象类型，以便反序列化时得到对应的具体对象
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        //断言，判断redisConnectionFactory是否不为空,实现InitializingBean接口会自动执行，所以这里不用写这行代码。
//        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 使用FastJsonredisSerializer
     * @param redisConnectionFactory
     * @return
     */
//----------todo    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisFastJsonTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
//        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
       FastjsonSerilizer fastJsonRedisSerializer=new FastjsonSerilizer(Object.class);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//       redis开启事务
        redisTemplate.setEnableTransactionSupport(true);
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
        //断言，判断redisConnectionFactory是否不为空,实现InitializingBean接口会自动执行，所以这里不用写这行代码。
//        redisTemplate.afterPropertiesSet();
//        log.info("spring redis info"+redisConnectionFactory.getClusterConnection().clusterGetClusterInfo());
//        log.info("spring redis Nodes:"+redisConnectionFactory.getClusterConnection().clusterGetNodes());
        return redisTemplate;
    }

    /**
     * 自定义fastjson反序列化
     * @param redisConnectionFactory
     * @return
     */
//    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> defineFastJsonTempalte(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        FastjsonSerilizer fastJsonRedisSerializer=new FastjsonSerilizer(Object.class);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//       redis开启事务
        redisTemplate.setEnableTransactionSupport(true);
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        // hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
        //断言，判断redisConnectionFactory是否不为空,实现InitializingBean接口会自动执行，所以这里不用写这行代码。
//        redisTemplate.afterPropertiesSet();
//        log.info("spring redis info"+redisConnectionFactory.getClusterConnection().clusterGetClusterInfo());
//        log.info("spring redis Nodes:"+redisConnectionFactory.getClusterConnection().clusterGetNodes());
        return redisTemplate;
    }














    /**
     * 重写缓存key
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuffer sb = new StringBuffer();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();

            }
        };

    }
}